#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent),
    receiverThread(new QThread)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    UiInit();
    //try_demo();
    init_baud();
    init_btn();
    init_connect();
    ui->wid_plot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom);
    ui->wid_plot->addGraph();
    ui->wid_plot->graph(0)->setPen(QPen(Qt::blue)); // Line color blue for the graph
    ui->wid_plot->graph(0)->setBrush(QBrush(QColor(0, 0, 255, 20))); // Filled with translucent blue

    // Create data containers
    m_xData.clear();
    m_yData.clear();

    // Setup timer to update the plot every 16ms (roughly 60Hz)
    m_updateTimer = new QTimer(this);
    connect(m_updateTimer, &QTimer::timeout, this, &MainWindow::updateGraph);
    m_updateTimer->start(1);  // Update at 60Hz (every 16ms)


}

void MainWindow::updateGraph()
{
    // 锁定数据保护线程安全
    QMutexLocker locker(&m_dataMutex);

    if (m_yData.isEmpty()) {
        return;  // 如果没有数据，返回
    }

    // 创建存储最近250个数据点的数组
    QVector<double> xData, yData;

    // 控制显示的数据点数量，最多显示250个数据点
    int numPoints = qMin(250, m_yData.size());

    for (int i = 0; i < numPoints; ++i) {
        xData.append(m_xData[i]);  // 时间轴数据
        yData.append(m_yData[i]);  // 浮动数据
    }

    // 获取第一个图形并设置其数据
    auto graph = ui->wid_plot->graph(0);
    graph->setData(xData, yData);  // 更新图形数据

    // 设置图形样式（线条颜色、填充颜色、透明度）
    graph->setPen(QPen(Qt::blue));  // 设置线条颜色为蓝色
    graph->setBrush(QBrush(QColor(0, 0, 255, 20)));  // 设置填充色为蓝色，透明度为20

    // 设置线条类型为平滑曲线
    graph->setLineStyle(QCPGraph::lsLine);  // 设置为线条样式
    graph->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssNone));  // 无数据点

    // 重新计算坐标轴范围以适应数据
    graph->rescaleAxes();

    // 重绘图形
    ui->wid_plot->replot();
}


MainWindow::~MainWindow()
{
    if (receiver) {
        receiver->deleteLater();
    }
    if (receiverThread) {
        receiverThread->quit();
        receiverThread->wait();
        delete receiverThread;
    }
    delete ui;
}

// 初始化信号槽
void MainWindow::init_connect()
{
    connect(this->ui->btn_open, &QPushButton::clicked,this, &MainWindow::btn_opened);
    connect(this->ui->btn_flush, &QPushButton::clicked, this, &MainWindow::btn_flushed);
    connect(this->ui->btn_set_zero,&QPushButton::clicked,this,[=](){
        QByteArray temp;
        // temp.append(static_cast<char>(0x49));
        // temp.append(static_cast<char>(0x44));
        temp.append(static_cast<char>(0x01));
        temp.append(static_cast<char>(0x10));
        temp.append(static_cast<char>(0x00));
        temp.append(static_cast<char>(0x99));
        temp.append(static_cast<char>(0x00));
        temp.append(static_cast<char>(0x01));
        temp.append(static_cast<char>(0x02));
        temp.append(static_cast<char>(0x00));
        temp.append(static_cast<char>(0x00));
        temp.append(static_cast<char>(0xBB));
        temp.append(static_cast<char>(0x99));
        emit command(temp);
    });

}

// 初始化按键
void MainWindow::init_btn()
{
    ui->com_baud->clear();
    // 获取所有可用的串口信息
    const auto serialPortInfos = QSerialPortInfo::availablePorts();
    QStringList portList;
    // 筛选出描述中包含 "Virtual" 的串口设备
    for (const QSerialPortInfo &info : serialPortInfos) {
        ui->com_ser->addItem(info.portName()+ " (" + info.description() + ")");
        Qser_map.insert(info.portName()+ " (" + info.description() + ")",info);
    }
    ui->com_baud->addItems(Qser_baud);

}

void MainWindow::init_baud()
{
    Qser_baud.append("9600");
    Qser_baud.append("38400");
    Qser_baud.append("460800");
    Qser_baud.append("115200");
    Qser_baud.append("921600");
    Qser_baud.append("1000000");
}


// 示例demo
void MainWindow::try_demo()
{
    // add two new graphs and set their look:
    ui->wid_plot->addGraph();
    ui->wid_plot->graph(0)->setPen(QPen(Qt::blue)); // line color blue for first graph
    ui->wid_plot->graph(0)->setBrush(QBrush(QColor(0, 0, 255, 20))); // first graph will be filled with translucent blue
    ui->wid_plot->addGraph();
    ui->wid_plot->graph(1)->setPen(QPen(Qt::red)); // line color red for second graph
    // generate some points of data (y0 for first, y1 for second graph):
    QVector<double> x(251), y0(251), y1(251);
    for (int i=0; i<251; ++i)
    {
        x[i] = i;
        y0[i] = qExp(-i/150.0)*qCos(i/10.0); // exponentially decaying cosine
        y1[i] = qExp(-i/150.0);              // exponential envelope
    }
    // configure right and top axis to show ticks but no labels:
    // (see QCPAxisRect::setupFullAxesBox for a quicker method to do this)
    ui->wid_plot->xAxis2->setVisible(true);
    ui->wid_plot->xAxis2->setTickLabels(false);
    ui->wid_plot->yAxis2->setVisible(true);
    ui->wid_plot->yAxis2->setTickLabels(false);
    // make left and bottom axes always transfer their ranges to right and top axes:
    connect(ui->wid_plot->xAxis, SIGNAL(rangeChanged(QCPRange)), ui->wid_plot->xAxis2, SLOT(setRange(QCPRange)));
    connect(ui->wid_plot->yAxis, SIGNAL(rangeChanged(QCPRange)), ui->wid_plot->yAxis2, SLOT(setRange(QCPRange)));
    // pass data points to graphs:
    ui->wid_plot->graph(0)->setData(x, y0);
    ui->wid_plot->graph(1)->setData(x, y1);
    // let the ranges scale themselves so graph 0 fits perfectly in the visible area:
    ui->wid_plot->graph(0)->rescaleAxes();
    // same thing for graph 1, but only enlarge ranges (in case graph 1 is smaller than graph 0):
    ui->wid_plot->graph(1)->rescaleAxes(true);
    // Note: we could have also just called ui->wid_plot->rescaleAxes(); instead
    // Allow user to drag axis ranges with mouse, zoom with mouse wheel and select graphs by clicking:
    ui->wid_plot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectPlottables);
}

// 开始按键
void MainWindow::btn_opened()
{
    con = Con::connected;
    setSerial(); //设置串口信息
    qDebug()<<ui->com_ser->currentText();
    qDebug()<<Qser_map[ui->com_ser->currentText()].portName();
}
void MainWindow::setSerial()
{

    receiver = SerialWorker::instance(ui->com_baud->currentText().toInt(), Qser_map[ui->com_ser->currentText()].portName());  // 假设 vir_name 是你选择的串口名称
    receiver->SerialSet(ui->com_baud->currentText().toInt(), Qser_map[ui->com_ser->currentText()].portName());

    receiver->moveToThread(receiverThread);
    connect(receiverThread, &QThread::started, receiver, &SerialWorker::start);
    connect(receiver,&SerialWorker::showMessage,this,&MainWindow::showMessageBox);
    connect(this,&MainWindow::command,receiver,&SerialWorker::doDataSendWork, Qt::QueuedConnection);
    connect(receiver, &SerialWorker::display_data, this, [=](QByteArray rece) {
        // 将接收到的数据存入队列
        m_dataQueue.enqueue(rece);

        // 如果队列大小超过 2000，删除最前面的数据
        if (m_dataQueue.size() > 2000) {
            m_dataQueue.dequeue();
        }

        // 获取队列中的一个数据
        QByteArray data = m_dataQueue.dequeue();

        // 按字节显示 16 进制数据
        QString hexStr;
        for (int i = 0; i < data.size(); ++i) {
            hexStr += QString::asprintf("%02X ", static_cast<unsigned char>(data.at(i)));
        }
        connect(this,&MainWindow::serial_finish,receiver,&SerialWorker::stop_serial);

        // 显示到界面
        ui->text_log->append(hexStr);
    });

    connect(receiver,&SerialWorker::finished,this,[=](){
        if (receiverThread->isRunning())
        {
            receiverThread->requestInterruption();
            receiverThread->quit();  // 停止事件循环
            receiverThread->wait();  // 等待线程结束
            qDebug()<<"接收线程已关闭";
        }
    });
    receiverThread->start();
}

void MainWindow::showMessageBox(const QString &message)
{
    QMessageBox::information(this, "初始化", message);
}

void MainWindow::processReceivedData()
{
}

void MainWindow::btn_flushed()
{
    switch(con)
    {
        case Con::connected:

            ui->com_ser->clear();
            Qser_map.clear();
            init_btn();
            break;
        case Con::disconnected:
            qDebug()<<"处于未连接状态";
            ui->com_ser->clear();
            Qser_map.clear();
            init_btn();
            break;
    }
    emit serial_finish();

}


void MainWindow::onDataReceived(const QByteArray &data)
{
    // // 先将数据转为字符串
    // QString strData = QString::fromUtf8(data);

    // // 正则表达式提取 group 名称和对应数据
    // QRegularExpression regex("group(\\d+):(\\d+)");
    // QRegularExpressionMatchIterator i = regex.globalMatch(strData);

    // QMutexLocker locker(&m_dataMutex);  // 锁定数据保护线程安全

    // // 获取当前的时间戳作为 x 轴
    // double currentTime = QDateTime::currentMSecsSinceEpoch() / 1000.0;  // 时间单位为秒

    // while (i.hasNext()) {
    //     QRegularExpressionMatch match = i.next();
    //     QString groupName = match.captured(1);  // group序号 (例如 group1, group2)
    //     int value = match.captured(2).toInt(); // 对应的数据值

    //     // 将数据存入对应的 group
    //     m_groupData[groupName].append(value);

    //     // 控制每个 group 的最大数据点数，避免无限增长
    //     if (m_groupData[groupName].size() > 250) {
    //         m_groupData[groupName].removeFirst();  // 移除最旧的数据
    //     }

    //     // 将时间戳添加到 x 轴的数据列表
    //     m_xData.append(currentTime);

    //     // 根据 group 更新相应的曲线
    //     updateGroupCurve(groupName);
    // }

    // // 控制 x 轴数据点数量（如同上）
    // if (m_xData.size() > 250) {
    //     m_xData.removeFirst();
    // }

    // bool ok;
    // int value = data.toInt(&ok);
    // ui->text_log->append(data);
    // if(ok)
    // {
    //     {
    //         QMutexLocker locker(&m_dataMutex);  // 锁定数据保护线程安全

    //         // 时间戳作为 x 轴，增加到 m_xData
    //         m_xData.append(QDateTime::currentMSecsSinceEpoch() / 1000.0);  // 时间单位为秒

    //         // 存储数据到 y 轴
    //         m_yData.append(value);

    //         // 控制最大数据点数，例如最多显示250个数据点
    //         if (m_xData.size() > 250) {
    //             m_xData.removeFirst();  // 移除最旧的 x 数据
    //             m_yData.removeFirst();  // 移除最旧的 y 数据
    //         }
    //     }
    // }
}

void MainWindow::UiInit()
{
    QStringList files;
    files<<"AMOLED.qss"<<"Aqua.qss"<<"ConsoleStyle.qss"<<"ElegantDark.qss"<<"MacOS.qss"<<"ManjaroMix.qss"
          <<"MaterialDark.qss"<<"MetroUI.qss"<<"NeonButtons.qss"<<"Ubuntu.qss";
    QString path= QString(":/qss/%1").arg(files.at(4));
    QFile file(path);
    if (!file.open(QIODevice::ReadOnly))
        exit(0);

    QTextStream in(&file);
    QString css = in.readAll();
    //应用程序对象的QApplication::setStyleSheet
    qApp->setStyleSheet(css);
}


// 清空text内容
void MainWindow::on_btn_clear_text_clicked()
{
    ui->text_log->clear();
}

